#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H

#include <functional>
#include <vector>
#include <map>
#include <memory> //std::shared_ptr    std::unique_ptr  std::weak_ptr
#include <mutex>

#include <iostream>
#include <assert.h>

#include <algorithm>

#include "ThreadPool.h"
#include "SignalHandle.h"
#include "Singleton.h"

class noncopyable
{
public:
    noncopyable(const noncopyable&) = delete;
    void operator=(const noncopyable&) = delete;

protected:
    noncopyable() = default;
    ~noncopyable() = default;
};


/**
 * SignalImpl 管理 SlotImpl 数组
 * SlotImpl 记录回调函数
 * Signal 一个信号发出后，执行该信号下的多个回调函数
 */

/**
 * todo
 *
 * 1.多线程实现异步信号调用
 * 2.实现信号管理类，管理多个信号（通过map管理），通过枚举实现对信号的映射，
 * 3.使用单例模式实现信号管理类
 * 4.支持注册时，选择是否异步执行
 * 5.支持发送信号时，如果选择异步，则所有回调异步执行
 *
 */


namespace muduo
{

namespace detail
{

template<typename Callback>
struct SlotImpl;

template<typename Callback>
struct SignalImpl : noncopyable
{
    typedef std::vector<std::weak_ptr<SlotImpl<Callback> > > SlotList;

    SignalImpl()
        : slots_(new SlotList)
    {
    }

    void copyOnWrite()
    {
        //mutex_.assertLocked();
        if (!slots_.unique())
        {
            slots_.reset(new SlotList(*slots_));
        }
        assert(slots_.unique());
    }

    //清理掉已经被释放掉的槽函数
    void clean()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        copyOnWrite();
        SlotList& list(*slots_);
        typename SlotList::iterator it(list.begin());
        while (it != list.end())
        {
            if (it->expired())
            {
                it = list.erase(it);
            }
            else
            {
                ++it;
            }
        }
    }

    std::mutex mutex_;
    std::shared_ptr<SlotList> slots_;
};

template<typename Callback>
struct SlotImpl : noncopyable
{
    typedef SignalImpl<Callback> Data;
    SlotImpl(const std::shared_ptr<Data>& data, Callback&& cb, bool async)
        : data_(data), cb_(cb), tie_(), tied_(false), index_(0), async_(async)
    {
    }

    SlotImpl(const std::shared_ptr<Data>& data, Callback&& cb,
             const std::shared_ptr<void>& tie, bool async)
        : data_(data), cb_(cb), tie_(tie), tied_(true), index_(0), async_(async)
    {
    }

    ~SlotImpl()
    {
        std::shared_ptr<Data> data(data_.lock());
        //printf("SlotImpl destruction index is %d\n",index_);
        if (data)
        {
            //槽函数被析构后，清理掉槽函数列表容器中对应的weak_ptr
            data->clean();
        }
    }

    std::weak_ptr<Data> data_;
    Callback cb_;
    std::weak_ptr<void> tie_;
    bool tied_;
    size_t index_;
    bool async_;
};

}

/// This is the handle for a slot
///
/// The slot will remain connected to the signal fot the life time of the
/// returned Slot object (and its copies).
typedef std::shared_ptr<void> Slot;

template<typename Signature>
class Signal;

template <typename RET, typename... ARGS>
class Signal<RET(ARGS...)> : noncopyable
{
public:
    typedef std::function<void (ARGS...)> Callback;
    typedef detail::SignalImpl<Callback> SignalImpl;
    typedef detail::SlotImpl<Callback> SlotImpl;

    Signal()
        : impl_(new SignalImpl), pool_(new ThreadPool(4))
    {
    }

    ~Signal()
    {
    }

    Slot connect(Callback&& func, bool async = false)
    {
        std::shared_ptr<SlotImpl> slotImpl(
            new SlotImpl(impl_, std::forward<Callback>(func), async));
        add(slotImpl);
        return slotImpl;
    }

    /**
     * @brief connect
     * @param func
     * @param tie  tie 成员通过 std::weak_ptr 检查对象的生命周期，避免信号槽机制中调用无效对象的成员函数，假设我们有一个对象 Foo，
     *             它连接了一个信号，但对象可能在信号发射前被销毁。我们需要确保信号触发时不会调用已销毁对象的成员函数。
     *
     * @return
     */
    Slot connect(Callback&& func, const std::shared_ptr<void>& tie, bool async = false)
    {
        std::shared_ptr<SlotImpl> slotImpl(new SlotImpl(impl_, std::forward<Callback>(func), tie, async));
        add(slotImpl);
        return slotImpl;
    }

    void disconnect(const std::shared_ptr<void>& slot)
    {
        remove(std::static_pointer_cast<SlotImpl>(slot));
    }

    void call(ARGS&&... args)
    {
        SignalImpl& impl(*impl_);
        std::shared_ptr<typename SignalImpl::SlotList> slots;
        {
            std::lock_guard<std::mutex> lock(impl.mutex_);
            slots = impl.slots_;
        }
        typename SignalImpl::SlotList& s(*slots);
        for (typename SignalImpl::SlotList::const_iterator it = s.begin(); it != s.end(); ++it)
        {
            std::shared_ptr<SlotImpl> slotImpl = it->lock();
            if (slotImpl)
            {
                std::weak_ptr<SlotImpl> slot_weak = it->lock();
                if (slotImpl->async_)
                {
                    //pool_->submit(std::bind(&Signal::__call, this, slot_weak), args...);
                    pool_->submit(std::bind(&Signal::__call, this, slot_weak, args...));
                }
                else
                {
                    __call(slot_weak, args...);
                }
            }

//            std::shared_ptr<SlotImpl> slotImpl = it->lock();//尝试返回一个有效的std::shared_ptr,如果对象已经被释放,则返回空的std::shared_ptr
//            if (slotImpl)
//            {
//                std::shared_ptr<void> guard;
//                if (slotImpl->tied_)
//                {
//                    guard = slotImpl->tie_.lock();
//                    if (guard)
//                    {
//                        slotImpl->cb_(args...);
//                    }
//                }
//                else
//                {
//                    slotImpl->cb_(args...);
//                }
//            }
        }
    }

private:

    void add(const std::shared_ptr<SlotImpl>& slot)
    {
        SignalImpl& impl(*impl_);
        {
            std::lock_guard<std::mutex> lock(impl.mutex_);
            impl.copyOnWrite();
            slot->index_ = impl.slots_->size();
            impl.slots_->push_back(slot);
        }
    }

    void remove(const std::shared_ptr<SlotImpl>& slot)
    {
        SignalImpl& impl(*impl_);
        {
            std::lock_guard<std::mutex> lock(impl.mutex_);
            impl.copyOnWrite();

            auto it = std::find_if(impl.slots_->begin(), impl.slots_->end(), [&slot](auto& item){
                return item.lock() == slot;
            });

            if (it != impl.slots_->end())
                impl.slots_->erase(it);
        }
    }

    void __call(std::weak_ptr<SlotImpl>& s, ARGS&&... args)
    {
        std::shared_ptr<SlotImpl> slotImpl = s.lock();
        if (slotImpl)
        {
            std::shared_ptr<void> guard;
            if (slotImpl->tied_)
            {
                guard = slotImpl->tie_.lock();
                if (guard)
                {
                    slotImpl->cb_(args...);
                }
            }
            else
            {
                slotImpl->cb_(args...);
            }
        }
    }

    const std::shared_ptr<SignalImpl> impl_;
    std::shared_ptr<ThreadPool> pool_;
};



class SIGNAL
{
public:
    SIGNAL() : pool_(new ThreadPool(4))
    {}

    void testSingleton()
    {
        printf("test Singleton");
    }



private:
    std::map<Sig, std::shared_ptr< muduo::Signal<void()> >> signals_;

    std::shared_ptr<ThreadPool> pool_;
};

static void RegisterExtension()
{
    Singleton_s<SIGNAL>::getInstance();
}
static void CallExtension(Sig sig)
{
    SIGNAL singles = Singleton_s<SIGNAL>::getInstance();
    singles.testSingleton();
    printf("this sig is %d\n", static_cast<int>(sig));
}

}

#endif // SIGNAL_SLOT_H

